using System;
using System.Collections.Generic;
using System.Text;

using RaisingStudio.Common.Reflection.TypesResolver;

namespace RaisingStudio.Common.Reflection
{
    public delegate object MethodInvokeHandler(object target, object[] paramters);

    /// <summary>
    /// A type manager。
    /// </summary>
    public class TypeManager
    {
        private static readonly ITypeResolver _internalTypeResolver = new CachedTypeResolver(new TypeResolver());

        public static Dictionary<string, System.Type> WellKnownDataTypes;
        public static Dictionary<System.Type, string> WellKnownDataTypeNames;
        public static Dictionary<string, System.Data.DbType> WellKnownDbTypes;
        public static Dictionary<System.Data.DbType, string> WellKnownDbTypeNames;

        static TypeManager()
        {
            #region WellKnownDataTypes
            WellKnownDataTypes = new Dictionary<string, System.Type>();
            WellKnownDataTypeNames = new Dictionary<System.Type, string>();
            WellKnownDataTypeNames.Add(typeof(short), "short");
            WellKnownDataTypes.Add("short", typeof(short));
            WellKnownDataTypes.Add("Short", typeof(short));
            WellKnownDataTypes.Add("SHORT", typeof(short));
            WellKnownDataTypes.Add("int16", typeof(short));
            WellKnownDataTypes.Add("Int16", typeof(short));
            WellKnownDataTypes.Add("INT16", typeof(short));

            WellKnownDataTypeNames.Add(typeof(int), "int");
            WellKnownDataTypes.Add("int", typeof(int));
            WellKnownDataTypes.Add("Int", typeof(int));
            WellKnownDataTypes.Add("INT", typeof(int));
            WellKnownDataTypes.Add("int32", typeof(int));
            WellKnownDataTypes.Add("Int32", typeof(int));
            WellKnownDataTypes.Add("INT32", typeof(int));

            WellKnownDataTypeNames.Add(typeof(long), "long");
            WellKnownDataTypes.Add("long", typeof(long));
            WellKnownDataTypes.Add("Long", typeof(long));
            WellKnownDataTypes.Add("LONG", typeof(long));
            WellKnownDataTypes.Add("int64", typeof(long));
            WellKnownDataTypes.Add("Int64", typeof(long));
            WellKnownDataTypes.Add("INT64", typeof(long));

            WellKnownDataTypeNames.Add(typeof(byte), "byte");
            WellKnownDataTypes.Add("byte", typeof(byte));
            WellKnownDataTypes.Add("Byte", typeof(byte));
            WellKnownDataTypes.Add("BYTE", typeof(byte));

            WellKnownDataTypeNames.Add(typeof(byte[]), "byte[]");
            WellKnownDataTypes.Add("byte[]", typeof(byte[]));
            WellKnownDataTypes.Add("Byte[]", typeof(byte[]));
            WellKnownDataTypes.Add("BYTE[]", typeof(byte[]));
            WellKnownDataTypes.Add("image", typeof(byte[]));
            WellKnownDataTypes.Add("Image", typeof(byte[]));
            WellKnownDataTypes.Add("IMAGE", typeof(byte[]));
            WellKnownDataTypes.Add("binary", typeof(byte[]));
            WellKnownDataTypes.Add("Binary", typeof(byte[]));
            WellKnownDataTypes.Add("BINARY", typeof(byte[]));

            WellKnownDataTypeNames.Add(typeof(bool), "bool");
            WellKnownDataTypes.Add("bool", typeof(bool));
            WellKnownDataTypes.Add("Bool", typeof(bool));
            WellKnownDataTypes.Add("BOOL", typeof(bool));
            WellKnownDataTypes.Add("boolean", typeof(bool));
            WellKnownDataTypes.Add("Boolean", typeof(bool));
            WellKnownDataTypes.Add("BOOLEAN", typeof(bool));

            WellKnownDataTypeNames.Add(typeof(float), "float");
            WellKnownDataTypes.Add("float", typeof(float));
            WellKnownDataTypes.Add("Float", typeof(float));
            WellKnownDataTypes.Add("FLOAT", typeof(float));
            WellKnownDataTypes.Add("single", typeof(float));
            WellKnownDataTypes.Add("Single", typeof(float));
            WellKnownDataTypes.Add("SINGLE", typeof(float));

            WellKnownDataTypeNames.Add(typeof(double), "double");
            WellKnownDataTypes.Add("double", typeof(double));
            WellKnownDataTypes.Add("Double", typeof(double));
            WellKnownDataTypes.Add("DOUBLE", typeof(double));

            WellKnownDataTypeNames.Add(typeof(decimal), "decimal");
            WellKnownDataTypes.Add("decimal", typeof(decimal));
            WellKnownDataTypes.Add("Decimal", typeof(decimal));
            WellKnownDataTypes.Add("DECIMAL", typeof(decimal));

            WellKnownDataTypeNames.Add(typeof(DateTime), "DateTime");
            WellKnownDataTypes.Add("datetime", typeof(DateTime));
            WellKnownDataTypes.Add("dateTime", typeof(DateTime));
            WellKnownDataTypes.Add("DateTime", typeof(DateTime));
            WellKnownDataTypes.Add("DATETIME", typeof(DateTime));

            WellKnownDataTypeNames.Add(typeof(Guid), "Guid");
            WellKnownDataTypes.Add("guid", typeof(Guid));
            WellKnownDataTypes.Add("Guid", typeof(Guid));
            WellKnownDataTypes.Add("GUID", typeof(Guid));

            WellKnownDataTypeNames.Add(typeof(string), "string");
            WellKnownDataTypes.Add("string", typeof(string));
            WellKnownDataTypes.Add("String", typeof(string));
            WellKnownDataTypes.Add("STRING", typeof(string));

            WellKnownDataTypeNames.Add(typeof(object), "object");
            WellKnownDataTypes.Add("object", typeof(object));
            WellKnownDataTypes.Add("Object", typeof(object));
            WellKnownDataTypes.Add("OBJECT", typeof(object));
            #endregion
            #region WellKnownDbTypes
            WellKnownDbTypes = new Dictionary<string, System.Data.DbType>();
            WellKnownDbTypeNames = new Dictionary<System.Data.DbType, string>();
            WellKnownDbTypes.Add("short", System.Data.DbType.Int16);
            WellKnownDbTypes.Add("Short", System.Data.DbType.Int16);
            WellKnownDbTypes.Add("SHORT", System.Data.DbType.Int16);
            WellKnownDbTypes.Add("int16", System.Data.DbType.Int16);
            WellKnownDbTypes.Add("Int16", System.Data.DbType.Int16);
            WellKnownDbTypes.Add("INT16", System.Data.DbType.Int16);
            WellKnownDbTypeNames.Add(System.Data.DbType.Int16, "short");

            WellKnownDbTypes.Add("int", System.Data.DbType.Int32);
            WellKnownDbTypes.Add("Int", System.Data.DbType.Int32);
            WellKnownDbTypes.Add("INT", System.Data.DbType.Int32);
            WellKnownDbTypes.Add("int32", System.Data.DbType.Int32);
            WellKnownDbTypes.Add("Int32", System.Data.DbType.Int32);
            WellKnownDbTypes.Add("INT32", System.Data.DbType.Int32);
            WellKnownDbTypeNames.Add(System.Data.DbType.Int32, "int");

            WellKnownDbTypes.Add("long", System.Data.DbType.Int64);
            WellKnownDbTypes.Add("Long", System.Data.DbType.Int64);
            WellKnownDbTypes.Add("LONG", System.Data.DbType.Int64);
            WellKnownDbTypes.Add("int64", System.Data.DbType.Int64);
            WellKnownDbTypes.Add("Int64", System.Data.DbType.Int64);
            WellKnownDbTypes.Add("INT64", System.Data.DbType.Int64);
            WellKnownDbTypeNames.Add(System.Data.DbType.Int64, "long");

            WellKnownDbTypes.Add("byte", System.Data.DbType.Byte);
            WellKnownDbTypes.Add("Byte", System.Data.DbType.Byte);
            WellKnownDbTypes.Add("BYTE", System.Data.DbType.Byte);
            WellKnownDbTypeNames.Add(System.Data.DbType.Byte, "byte");

            WellKnownDbTypes.Add("byte[]", System.Data.DbType.Binary);
            WellKnownDbTypes.Add("Byte[]", System.Data.DbType.Binary);
            WellKnownDbTypes.Add("BYTE[]", System.Data.DbType.Binary);
            WellKnownDbTypes.Add("image", System.Data.DbType.Binary);
            WellKnownDbTypes.Add("Image", System.Data.DbType.Binary);
            WellKnownDbTypes.Add("IMAGE", System.Data.DbType.Binary);
            WellKnownDbTypes.Add("binary", System.Data.DbType.Binary);
            WellKnownDbTypes.Add("Binary", System.Data.DbType.Binary);
            WellKnownDbTypes.Add("BINARY", System.Data.DbType.Binary);
            WellKnownDbTypeNames.Add(System.Data.DbType.Binary, "byte[]");

            WellKnownDbTypes.Add("bool", System.Data.DbType.Boolean);
            WellKnownDbTypes.Add("Bool", System.Data.DbType.Boolean);
            WellKnownDbTypes.Add("BOOL", System.Data.DbType.Boolean);
            WellKnownDbTypes.Add("boolean", System.Data.DbType.Boolean);
            WellKnownDbTypes.Add("Boolean", System.Data.DbType.Boolean);
            WellKnownDbTypes.Add("BOOLEAN", System.Data.DbType.Boolean);
            WellKnownDbTypeNames.Add(System.Data.DbType.Boolean, "bool");

            WellKnownDbTypes.Add("float", System.Data.DbType.Single);
            WellKnownDbTypes.Add("Float", System.Data.DbType.Single);
            WellKnownDbTypes.Add("FLOAT", System.Data.DbType.Single);
            WellKnownDbTypes.Add("single", System.Data.DbType.Single);
            WellKnownDbTypes.Add("Single", System.Data.DbType.Single);
            WellKnownDbTypes.Add("SINGLE", System.Data.DbType.Single);
            WellKnownDbTypeNames.Add(System.Data.DbType.Single, "float");

            WellKnownDbTypes.Add("double", System.Data.DbType.Double);
            WellKnownDbTypes.Add("Double", System.Data.DbType.Double);
            WellKnownDbTypes.Add("DOUBLE", System.Data.DbType.Double);
            WellKnownDbTypeNames.Add(System.Data.DbType.Double, "double");

            WellKnownDbTypes.Add("decimal", System.Data.DbType.Decimal);
            WellKnownDbTypes.Add("Decimal", System.Data.DbType.Decimal);
            WellKnownDbTypes.Add("DECIMAL", System.Data.DbType.Decimal);
            WellKnownDbTypeNames.Add(System.Data.DbType.Decimal, "decimal");

            WellKnownDbTypes.Add("datetime", System.Data.DbType.DateTime);
            WellKnownDbTypes.Add("dateTime", System.Data.DbType.DateTime);
            WellKnownDbTypes.Add("DateTime", System.Data.DbType.DateTime);
            WellKnownDbTypes.Add("DATETIME", System.Data.DbType.DateTime);
            WellKnownDbTypeNames.Add(System.Data.DbType.DateTime, "DateTime");

            WellKnownDbTypes.Add("guid", System.Data.DbType.Guid);
            WellKnownDbTypes.Add("Guid", System.Data.DbType.Guid);
            WellKnownDbTypes.Add("GUID", System.Data.DbType.Guid);
            WellKnownDbTypeNames.Add(System.Data.DbType.Guid, "Guid");

            WellKnownDbTypes.Add("string", System.Data.DbType.String);
            WellKnownDbTypes.Add("String", System.Data.DbType.String);
            WellKnownDbTypes.Add("STRING", System.Data.DbType.String);
            WellKnownDbTypeNames.Add(System.Data.DbType.String, "string");

            WellKnownDbTypes.Add("object", System.Data.DbType.Object);
            WellKnownDbTypes.Add("Object", System.Data.DbType.Object);
            WellKnownDbTypes.Add("OBJECT", System.Data.DbType.Object);
            WellKnownDbTypeNames.Add(System.Data.DbType.Object, "object");
            #endregion
        }



        /// <summary>
        /// Gets Wellknown Type.
        /// </summary>
        /// <param name="name">Type name.</param>
        /// <returns>Type.</returns>
        public static System.Type GetWellKnownDataType(string name)
        {
#if use_registry
            return TypeRegistry.ResolveType(typeName);
#else
            name = name.Trim();
            if (WellKnownDataTypes.ContainsKey(name))
            {
                return WellKnownDataTypes[name];
            }
            return null;
#endif
        }

        public static string GetWellKnownDataTypeName(System.Type type)
        {
            if (WellKnownDataTypeNames.ContainsKey(type))
            {
                return WellKnownDataTypeNames[type];
            }
            return type.ToString();
        }

        /// <summary>
        /// Gets Wellknown Type.
        /// </summary>
        /// <param name="dataType">data Type name.</param>
        /// <returns>Data type.</returns>
        public static System.Data.DbType GetWellKnownDbType(string dbType)
        {
            return WellKnownDbTypes[dbType.Trim()];
        }

        public static string GetWellKnownDbTypeName(System.Data.DbType dbType)
        {
            if (WellKnownDbTypeNames.ContainsKey(dbType))
            {
                return WellKnownDbTypeNames[dbType];
            }
            return dbType.ToString();
        }

        /// <summary>
        /// Gets the specific name Type.
        /// </summary>
        /// <param name="instance">An instance.</param>
        /// <param name="typeName">Type name.</param>
        /// <returns>Type.</returns>
        public static System.Type GetType(object instance, string typeName)
        {
            typeName = typeName.Trim();
            System.Type type;
            System.Reflection.Assembly assembly;
            if (typeName.IndexOf(",") >= 0)
            {
                string[] names = typeName.Split(',');
                assembly = System.Reflection.Assembly.LoadFrom(names[0]);
                typeName = names[1];
            }
            else
            {
                if (instance != null)
                {
                    assembly = instance.GetType().Assembly;
                }
                else
                {
                    assembly = System.Reflection.Assembly.GetCallingAssembly();
                }
            }

            type = assembly.GetType(typeName);
            return type;
        }

        /// <summary>
        /// Gets the specific name Type.
        /// </summary>
        /// <param name="instance">Type.</param>
        /// <param name="typeName">Type name.</param>
        /// <returns>Type.</returns>
        public static System.Type GetType(System.Type type, string typeName)
        {
            typeName = typeName.Trim();
            System.Reflection.Assembly assembly;
            if (typeName.IndexOf(",") >= 0)
            {
                string[] names = typeName.Split(',');
                assembly = System.Reflection.Assembly.LoadFrom(names[0]);
                typeName = names[1];
            }
            else
            {
                assembly = type.Assembly;
            }

            return assembly.GetType(typeName);
        }

        /// <summary>
        /// Gets the specific name Type.
        /// </summary>
        /// <param name="typeName">Type name.</param>
        /// <returns>Type.</returns>
        public static System.Type GetType(string typeName)
        {
            return System.Type.GetType(typeName);
        }


        /// <summary>
        /// Resolves the supplied type name into a <see cref="System.Type"/>
        /// instance.
        /// </summary>
        /// <param name="typeName">
        /// The (possibly partially assembly qualified) name of a
        /// <see cref="System.Type"/>.
        /// </param>
        /// <returns>
        /// A resolved <see cref="System.Type"/> instance.
        /// </returns>
        /// <exception cref="System.TypeLoadException">
        /// If the type cannot be resolved.
        /// </exception>
        public static Type ResolveType(string typeName)
        {
            typeName = typeName.Trim();
            Type type = GetWellKnownDataType(typeName);
            if (type == null)
            {
                type = _internalTypeResolver.Resolve(typeName);
            }
            return type;
        }

        public static bool IsWellKnownDataType(System.Type type)
        {
            if ((type == typeof(string))
                || (type == typeof(int))
                || (type == typeof(DateTime))
                || (type == typeof(decimal))
                || (type == typeof(bool))
                || (type == typeof(long)) || (type == typeof(short))
                || (type == typeof(float)) || (type == typeof(double))
                || (type == typeof(byte)) || (type == typeof(byte[]))
                || (type == typeof(Guid)))
            {
                return true;
            }
            else
            {
                return false;
            }
        }

        public static object InvokeMethod(System.Reflection.MethodInfo methodInfo, object instance, object[] parameters)
        {
            throw new System.NotImplementedException("Not Implemented.");
            return null;
        }
    }
}
